package com.lp.article.listener;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lp.article.mapper.CollectsMapper;
import com.lp.article.mapper.LikesMapper;
import com.lp.model.config.AppResult;
import com.lp.model.config.CodeMsg;
import com.lp.model.constants.MQConstants;
import com.lp.article.mapper.ArticleStatMapper;
import com.lp.model.constants.RedisConstants;
import com.lp.model.entity.article.ArticleStat;
import com.lp.model.exception.ApplicationException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class ArticleListener {

    @Autowired
    private ArticleStatMapper articleStatMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private LikesMapper likesMapper;

    @Autowired
    private CollectsMapper collectsMapper;

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 监听播放量同步到 mysql
     * @param msg 文章 id 和 访问量
     */
    @SneakyThrows
    @RabbitListener(queues = MQConstants.ARTICLE_VISITS_QUEUE)
    public void visitsSynMysql(String msg) {
        //1.解析数据
        log.info("MQ 消息: 播放量同步消息 {}", msg);
        HashMap hashMap = objectMapper.readValue(msg, HashMap.class);
        Long articleId = objectMapper.convertValue(hashMap.get("articleId"), new TypeReference<Long>() {});
        Long visits = objectMapper.convertValue(hashMap.get("visits"), new TypeReference<Long>() {});
        //2.同步数据
        articleStatMapper.visitsIncr(articleId, visits);
        log.info("MQ 完成: 访问量 + 1 已同步到 mysql！");
    }

    /**
     * Ps: 这里之前使用 redis5 一直有问题(set 类型自动转 string)，换成 5.0.9 就没事了
     * 监听文章统计数据同步到 redis 上，并将 mysql 中的访问量数据 + 1
     * @param msg 文章统计对象
     */
    @SneakyThrows
    @RabbitListener(queues = MQConstants.ARTICLE_STAT_QUEUE)
    public void statSynRedis(String msg) {
        //1.解析数据
        log.info("MQ 消息: 文章统计数据同步消息 {}", msg);
        if(msg == null) {
            throw new ApplicationException(AppResult.fail(CodeMsg.FAIL_NULL_MSG));
        }
        ArticleStat articleStat = objectMapper.readValue(msg, ArticleStat.class);
        //2.同步数据
        //1) 访问量(顺便加一)
        long visits = articleStat.getVisits() + 1;
        redisTemplate.opsForValue().set(RedisConstants.ARTICLE_VISITS + articleStat.getArticleId(),
                String.valueOf(visits), 30, TimeUnit.DAYS);
        //2) 该文章的所有点赞状态
        syncLikesForRedisByArtId(articleStat.getArticleId());
        //3) 该文章的所有收藏状态
        syncCollectsForRedisByArtId(articleStat.getArticleId());
        //4) 评论量
        redisTemplate.opsForValue().set(RedisConstants.ARTICLE_COMMENT + articleStat.getArticleId(),
                String.valueOf(articleStat.getCommentCount()), 30, TimeUnit.DAYS);
        //5) 同步数据库访问量 + 1
        articleStatMapper.visitsIncr(articleStat.getArticleId(), visits);
        log.info("MQ 完成: 文章统计数据已同步到 redis，并且访问量 + 1 也同步到 mysql");
    }

    /**
     * 将该文章下的所有收藏用户都以 set 的形式存到 redis 上
     * 为了减少 redis 连续发请求，使用一个命令解决
     */
    private void syncCollectsForRedisByArtId(Long articleId) {
        List<Long> userIdFromCollects = collectsMapper.selectUserIdAllByArticleId(articleId);
        //该文章还没有收藏用户
        if(userIdFromCollects.size() == 0) {
            return;
        }
        String[] collects = new String[userIdFromCollects.size()];
        for(int i = 0; i < userIdFromCollects.size(); i++) {
            collects[i] = RedisConstants.ARTICLE_LIKE_VALUE + userIdFromCollects.get(i);
        }
        redisTemplate.opsForSet().add(RedisConstants.ARTICLE_COLLECT + articleId, collects);
        redisTemplate.expire(RedisConstants.ARTICLE_COLLECT + articleId, 30, TimeUnit.DAYS);
    }

    /**
     * 将该文章下的所有点赞用户都以 set 的形式存到 redis 上
     * 为了减少 redis 连续发请求，使用一个命令解决
     */
    private void syncLikesForRedisByArtId(Long articleId) {
        List<Long> userIdFromLike = likesMapper.selectUserIdAllByArticleId(articleId);
        //该文章还没有点赞用户
        if(userIdFromLike.size() == 0) {
            return;
        }
        String[] likes = new String[userIdFromLike.size()];
        for(int i = 0; i < userIdFromLike.size(); i++) {
            likes[i] = RedisConstants.ARTICLE_LIKE_VALUE + userIdFromLike.get(i);
        }
        redisTemplate.opsForSet().add(RedisConstants.ARTICLE_LIKE + articleId, likes);
        redisTemplate.expire(RedisConstants.ARTICLE_LIKE + articleId, 30, TimeUnit.DAYS);
    }




}
